//
//  AsyncTaskManager.m
//  AsyncTaskTrigger
//
//  Created by zhouzuosong on 2020/9/17.
//

#import "AsyncTaskManager.h"


static AsyncTaskManager * _manager;

/** 信号量 */
static dispatch_semaphore_t _kSemaphore;

/** group  */
static dispatch_group_t _kGroup;

static LXTaskActionBlock _operationCompletBlock;

static void (^kSemahporeHandler)(void) = ^{
    if (_kSemaphore) {
        dispatch_semaphore_signal(_kSemaphore);
    }
};

@interface AsyncTaskManager ()

/// 用于存放 串行任务
@property (nonatomic, strong) NSMutableArray *serialActionArray;

/// 用于存放并行任务
@property (nonatomic, strong) NSMutableArray *concurrentActionArray;

/// 请求队列 控制顺序展示弹窗
@property (nonatomic, strong) NSOperationQueue *operationQueue;

@end

@implementation AsyncTaskManager


+ (instancetype)shareManager{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _manager = [[AsyncTaskManager alloc] init];
    });
    return _manager;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _serialActionArray = [NSMutableArray array];
        _concurrentActionArray = [NSMutableArray array];
    }
    return self;
}

#pragma mark - *****  添加任务  *****
- (void)addSerialActionBlock:(LXTaskActionBlock)actionBlock{
    [self addSerialActionBlock:actionBlock completBlock:^{
        NSLog(@"kael -- 串行 -- 一个 任务完成");
    }];
}

- (void)addSerialActionBlock:(LXTaskActionBlock)actionBlock completBlock:(LXTaskActionBlock)completBlock{
    [self addAction:actionBlock completBlock:completBlock isSerial:YES];
}

- (void)addConcurrentActionBlock:(LXTaskActionBlock)actionBlock{
    [self addConcurrentActionBlock:actionBlock completBlock:^{
        NSLog(@"kael -- 并行 -- 一个任务完成");
    }];
}

- (void)addConcurrentActionBlock:(LXTaskActionBlock)actionBlock completBlock:(LXTaskActionBlock)completBlock{
    [self addAction:actionBlock completBlock:completBlock isSerial:NO];
}

- (void)addAction:(LXTaskActionBlock)actionBlock completBlock:(LXTaskActionBlock)completBlock isSerial:(BOOL)isSerial{
    if ([self isWorking]) {
        NSLog(@"当前正在执行任务，不允许添加新的任务");
        return;
    }
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        if (isSerial) {
            [self releseSemaphonre];
            _kSemaphore = dispatch_semaphore_create(0);
        }
        
        actionBlock();
        
        if (isSerial) {
            dispatch_semaphore_wait(_kSemaphore, DISPATCH_TIME_FOREVER);
        }
    }];
    
    [blockOperation setCompletionBlock:^{
        completBlock();
    }];
    
    if (isSerial) {
        [_serialActionArray addObject:blockOperation];
    }else{
        [_concurrentActionArray addObject:blockOperation];
    }
}

- (void)sendSemaphore{
    if (_kSemaphore) {
        dispatch_semaphore_signal(_kSemaphore);
    }
}

#pragma mark - *****  开始任务  *****

- (void)startSerialActionWithCompletBlock:(LXTaskActionBlock)completBlock{
    _operationCompletBlock = completBlock;
    _operationQueue.maxConcurrentOperationCount = 1;
    // 取消之前执行的任务
    [self cancelOperation];
    // 确保队列对象被创建
    [[self operationQueue] setSuspended:NO];
    
    // 释放信号量
    [self releseSemaphonre];
    
    // 准备添加并执行新的任务
    for (NSBlockOperation *blockOperation in _serialActionArray) {
        [_operationQueue addOperation:blockOperation];
    }
    
    __weak typeof(&*self)weakSelf = self;
    [_operationQueue addOperationWithBlock:^{
        [weakSelf stopOperation];
        completBlock();
    }];
}

- (void)startConcurrentActionWithCompletBlock:(LXTaskActionBlock)completBlock{

    _operationCompletBlock = completBlock;
    _operationQueue.maxConcurrentOperationCount = 10;
    // 取消之前执行的任务
    [self cancelOperation];
    // 确保队列对象被创建
    [[self operationQueue] setSuspended:NO];
    
    // 释放信号量
    [self releseSemaphonre];

    NSInteger semaphoreNum = _concurrentActionArray.count;
    _kSemaphore = dispatch_semaphore_create(0);

    // 准备添加并执行新的任务
    for (NSBlockOperation *blockOperation in _concurrentActionArray) {
        [_operationQueue addOperation:blockOperation];
    }
    __weak typeof(&*self)weakSelf = self;
    [_operationQueue addOperationWithBlock:^{
        for (int i=0; i < semaphoreNum; i++) {
            dispatch_semaphore_wait(_kSemaphore, DISPATCH_TIME_FOREVER);
        }
        [weakSelf stopOperation];
        completBlock();
    }];
}
#pragma mark - *****  Block Handler  *****

- (void)addSerialHandlerActionBlock:(LXSemaphoreBlock)actionBlock{
    [self addSerialHandlerActionBlock:actionBlock completBlock:^{
//        NSLog(@"kael -- 任务完成");
    }];
}

- (void)addSerialHandlerActionBlock:(LXSemaphoreBlock)actionBlock completBlock:(LXTaskActionBlock)completBlock{
    [self addHandler:actionBlock completBlock:completBlock isSerial:YES];
}

- (void)addConcurrentHandlerActionBlock:(LXSemaphoreBlock)actionBlock{
    [self addConcurrentHandlerActionBlock:actionBlock completBlock:^{
//        NSLog(@"kael -- 并行 -- 一个任务完成");
    }];
}

- (void)addConcurrentHandlerActionBlock:(LXSemaphoreBlock)actionBlock completBlock:(LXTaskActionBlock)completBlock{
    [self addHandler:actionBlock completBlock:completBlock isSerial:NO];
}

- (void)addHandler:(LXSemaphoreBlock)actionBlock completBlock:(LXTaskActionBlock)completBlock isSerial:(BOOL)isSerial{
    
    if ([self isWorking]) {
        NSLog(@"当前正在执行任务，不允许添加新的任务");
        return;
    }
    _operationCompletBlock = completBlock;

    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        
        if (isSerial) {
            [self releseSemaphonre];
            _kSemaphore = dispatch_semaphore_create(0);
        }
        
        actionBlock(kSemahporeHandler);
        
        if (isSerial) {
            dispatch_semaphore_wait(_kSemaphore, DISPATCH_TIME_FOREVER);
        }
    }];
    
    [blockOperation setCompletionBlock:^{
        completBlock();
    }];
    
    if (isSerial) {
        [_serialActionArray addObject:blockOperation];
    }else{
        [_concurrentActionArray addObject:blockOperation];
    }
    
}
#pragma mark - *****  状态处理  *****

-(void)releseSemaphonre{
    if (_kSemaphore) {
        while (dispatch_semaphore_signal(_kSemaphore) != 0) {
            dispatch_semaphore_signal(_kSemaphore);
        }
    }
}

- (BOOL)staySignal{
    
    if (_kSemaphore && dispatch_semaphore_signal(_kSemaphore) != 0) {
        return YES;
    }
    return NO;
}

- (BOOL)isWorking{
    if (_operationQueue) {
        return !_operationQueue.suspended;
    }
    return NO;
}

- (void)suspendWork{
    if (_operationQueue) {
        _operationQueue.suspended = YES;
    }
}

- (void)continueWork{
    if (_operationQueue && _operationQueue.suspended) {
        _operationQueue.suspended = NO;
    }
}

- (void)cancelOperation{
    if (_operationQueue) {
        [_operationQueue cancelAllOperations];
    }
}

- (void)stopOperation{
    if (_operationQueue && _operationQueue.operations.count == 0){
        //other operation
        [_operationQueue setSuspended:YES];
        // 清理任务
        [self.serialActionArray removeAllObjects];
        [self.concurrentActionArray removeAllObjects];

        NSLog(@"kael operationQueue finished stop");
    }
}

#pragma mark - *****  getter && setter  *****
- (NSOperationQueue *)operationQueue{
    if (!_operationQueue) {
        _operationQueue = [[NSOperationQueue alloc] init];
        _operationQueue.maxConcurrentOperationCount = 1;
        [_operationQueue addObserver:self forKeyPath:@"operations" options:0 context:nil];
    }
    return _operationQueue;
}

#pragma mark - *****  KVO 回调  *****

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if (object == _operationQueue && [keyPath isEqualToString:@"operations"])
    {
        if (_operationQueue && _operationQueue.operations.count == 0){
            //other operation
            [_operationQueue setSuspended:YES];
            // 清理任务
            [self.serialActionArray removeAllObjects];
            [self.concurrentActionArray removeAllObjects];

            NSLog(@"ksel operationQueue finished KVO");
        }
    }
    else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

@end
